﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ProfileActioner.cs" company="DissingNelleman">
//   
// All rights reserved. </copyright>
// <summary>
//   The profile actioner.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ServiceLibrary.Actioners.Profiles
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using NHibernate;
    using NHibernate.Criterion;

    using ServiceLibrary.Actioners.Locations;
    using ServiceLibrary.Enums;
    using ServiceLibrary.Models.Domain;
    using ServiceLibrary.Services.Commands;
    using ServiceLibrary.Services.Queries;
    using ServiceLibrary.Services.Results;

    /// <summary>
    /// The profile actioner.
    /// </summary>
    public class ProfileActioner : ActionerBase
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ProfileActioner"/> class.
        /// </summary>
        /// <param name="session">
        /// The session.
        /// </param>
        /// <param name="activeUser">
        /// The active user.
        /// </param>
        public ProfileActioner(ISession session, User activeUser)
            : base(session, activeUser)
        {
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The create default profile related object.
        /// </summary>
        public void CreateDefaultProfileRelatedObject()
        {
            var sectorActioner = new ProfileSectorActioner(this.Session, this.ActiveUser);
            var typeActioner = new ProfileTypeActioner(this.Session, this.ActiveUser);
            var psActioner = new ProfileSubtypeActioner(this.Session, this.ActiveUser);
            Guid tmpID;

            IList<ProfileSector> sectors = this.Session.QueryOver<ProfileSector>().List();

            // Setup of private sector/types
            bool exists = (from s in sectors where s.Name.Equals("Privat") select s).Any();
            if (!exists)
            {
                tmpID = sectorActioner.CreateSector("Privat");
                ProfileSector privatSektor = sectorActioner.GetSector(tmpID);

                var privateTypes = new List<string> { "R&D (GTS)", "Hospital", "Plejehjem", "Uddannelse", "Anden" };
                foreach (string typeName in privateTypes)
                {
                    Guid typeID = typeActioner.CreateProfileType(typeName, privatSektor);
                    ProfileType pt = typeActioner.GetProfileType(typeID);
                    psActioner.CreateProfileSubtype(string.Empty, pt);
                }

                privateTypes = new List<string>
                                   {
                                       "Stor virksomhed", 
                                       "SME virksomhed", 
                                       "Iværksætter", 
                                       "Præ-iværksætter"
                                   };
                var privateSubTypes = new List<string>
                                          {
                                              "Teknologi (IT-baseret)", 
                                              "Teknologi (Rehab)", 
                                              "Teknologi (Drift)", 
                                              "Teknologi (Andet)", 
                                              "Service", 
                                              "Anden"
                                          };
                foreach (string typeName in privateTypes)
                {
                    Guid typeID = typeActioner.CreateProfileType(typeName, privatSektor);
                    ProfileType pt = typeActioner.GetProfileType(typeID);
                    foreach (string subTypeName in privateSubTypes)
                    {
                        psActioner.CreateProfileSubtype(subTypeName, pt);
                    }
                }
            }

            exists = (from s in sectors where s.Name.Equals("Offentlig") select s).Any();
            if (!exists)
            {
                // Setup of public sector/types
                tmpID = sectorActioner.CreateSector("Offentlig");
                ProfileSector offentligSektor = sectorActioner.GetSector(tmpID);
                var publicTypes = new List<string>
                                      {
                                          "Kommune", 
                                          "Hospital", 
                                          "Universitet", 
                                          "Anden uddannelse", 
                                          "Plejehjem", 
                                          "Anden"
                                      };

                foreach (string typeName in publicTypes)
                {
                    Guid typeID = typeActioner.CreateProfileType(typeName, offentligSektor);
                    ProfileType pt = typeActioner.GetProfileType(typeID);
                    psActioner.CreateProfileSubtype(string.Empty, pt);
                }
            }

            exists = (from s in sectors where s.Name.Equals("Andre") select s).Any();
            if (!exists)
            {
                // Setup of other sector/types
                tmpID = sectorActioner.CreateSector("Andre");
                ProfileSector andreSektor = sectorActioner.GetSector(tmpID);
                var otherTypes = new List<string>
                                     {
                                         "Branche organistation", 
                                         "Interesse organisation", 
                                         "Finansieringsservice", 
                                         "Klynge"
                                     };
                foreach (string typeName in otherTypes)
                {
                    Guid typeID = typeActioner.CreateProfileType(typeName, andreSektor);
                    ProfileType pt = typeActioner.GetProfileType(typeID);
                    psActioner.CreateProfileSubtype(string.Empty, pt);
                }

                // tmpID = sectorActioner.CreateSector("Potentiel Netværksdeltager");
            }

            // Setup of profile relations
            var relationActioner = new ProfileRelationActioner(this.Session, this.ActiveUser);
            IList<ProfileRelation> relations = this.Session.QueryOver<ProfileRelation>().List();
            var relationTypes = new List<string> { "Medlem", "Potentielt medlem", "Anden" };
            foreach (string type in relationTypes)
            {
                bool alreadyExists = (from r in relations where r.Type.Equals(type) select r).Any();
                if (!alreadyExists)
                {
                    relationActioner.CreateProfileRelation(type);
                }
            }

            var ctypes = new List<string>
                             {
                                 "Læst om", 
                                 "Mødt på messe/konferencer eller lign.", 
                                 "Telefonisk kontakt", 
                                 "Virksomhedsbesøg"
                             };
            var ctActioner = new ProfileContactTypeActioner(this.Session, this.ActiveUser);
            IList<ProfileContactType> pctypes = this.Session.QueryOver<ProfileContactType>().List();
            foreach (string ctyp in ctypes)
            {
                bool alreadyExists = (from p in pctypes where p.Type.Equals(ctyp) select p).Any();
                if (!alreadyExists)
                {
                    ctActioner.CreateProfileContactType(ctyp);
                }
            }
        }

        /// <summary>
        /// The create profile.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        public void CreateProfile(CreateProfileCommand command)
        {
            Services.Dto.Profile newProfile = command.Profile;

            if (newProfile != null && newProfile.ID != Guid.Empty)
            {
                var psa = new ProfileSubtypeActioner(this.Session, null);
                ProfileSubtype subtype = psa.GetProfileSubtype(newProfile.SubType);

                var pra = new ProfileRelationActioner(this.Session, null);
                ProfileRelation relation = pra.GetProfileRelation(newProfile.Relation);

                var la = new LocationActioner(this.Session, null);
                Location location = la.GetLocation(newProfile.Location);

                var profile = new Profile();
                profile.ID = newProfile.ID;
                profile.Name = newProfile.Name;
                profile.IsRemoteArea = newProfile.IsRemoteArea ?? false;
                profile.IsEntrepreneur = newProfile.IsEntrepreneur ?? false;
                profile.Name = newProfile.Name;
                profile.CVR = newProfile.CVR;
                profile.Relation = relation;
                profile.Location = location;
                profile.SubType = subtype;
                profile.CreatedDateTime = DateTime.Now;
                profile.Within150km = newProfile.Within150Km ?? false;

                if (newProfile.Region != null && newProfile.Region != Guid.Empty)
                {
                    Region reg = this.Session.Get<Region>(newProfile.Region);
                    if (reg != null)
                    {
                        profile.Region = reg;
                    }
                }

                if (newProfile.ContactType.HasValue && newProfile.ContactType.Value != Guid.Empty)
                {
                    var pcta = new ProfileContactTypeActioner(this.Session, null);
                    ProfileContactType contactType = pcta.GetProfileContactType(newProfile.ContactType.Value);
                    profile.ContactType = contactType;
                }

                this.Session.Save(profile);

                this.Log.CreateLogEntry(
                    profile.ID, string.Format("Profil '{0}', blev oprettet.", profile.Name), Enums.LogEntryType.Profile);
            }
        }

        /// <summary>
        /// The execute search query.
        /// </summary>
        /// <param name="query">
        /// The query.
        /// </param>
        /// <returns>
        /// The <see cref="ProfilesResult"/>.
        /// </returns>
        public ProfilesResult ExecuteSearchQuery(ProfilesQuery query)
        {
            SearchSettings settings = query.Settings;

            var result = new ProfilesResult();

            IQueryOver<Profile, Profile> search = this.Session.QueryOver<Profile>();
            this.ApplyDefaultCriterias(settings, search);

            List<Profile> s = this.Sort(settings, search.List()).ToList();

            int rowCount = search.RowCount();

            if (settings != null && settings.Skip != null && settings.Take != null && settings.Take > 0)
            {
                s = s.Skip((int)settings.Skip).Take((int)settings.Take).ToList();
            }

            result.TotalRowCount = rowCount;

            foreach (Profile profile in s)
            {
                Services.Dto.Profile dto = Services.Dto.Profile.Convert(profile);
                if (dto != null)
                {
                    result.List.Add(dto);
                }
            }

            return result;
        }

        /// <summary>
        /// The get profile.
        /// </summary>
        /// <param name="id">
        /// The id.
        /// </param>
        /// <returns>
        /// The <see cref="Profile"/>.
        /// </returns>
        public Profile GetProfile(Guid id)
        {
            var profile = this.Session.Get<Profile>(id);

            return profile;
        }

        /// <summary>
        /// The sort.
        /// </summary>
        /// <param name="settings">
        /// The settings.
        /// </param>
        /// <param name="list">
        /// The list.
        /// </param>
        /// <returns>
        /// The ordered list of profiles.
        /// </returns>
        public IOrderedEnumerable<Profile> Sort(SearchSettings settings, IList<Profile> list)
        {
            bool asc = settings.SortAscending == null || (bool)settings.SortAscending;

            if (settings.OrderBy != null)
            {
                switch (settings.OrderBy)
                {
                    case "Relation":
                        return asc ? list.OrderBy(x => x.Relation.Type) : list.OrderByDescending(x => x.Relation.Type);

                    case "Name":
                        return asc ? list.OrderBy(x => x.Name) : list.OrderByDescending(x => x.Name);

                    case "Sector":
                        return asc
                                    ? list.OrderBy(x => x.SubType.Type.Sector.Name)
                                    : list.OrderByDescending(x => x.SubType.Type.Sector.Name);

                    case "Type":
                        return asc
                                    ? list.OrderBy(x => x.SubType.Type.Name).ThenBy(x => x.SubType.Name)
                                    : list.OrderByDescending(x => x.SubType.Type.Name)
                                          .ThenByDescending(x => x.SubType.Name);
                }
            }

            return asc ? list.OrderBy(x => x.Name) : list.OrderByDescending(x => x.Name);
        }

        /// <summary>
        /// The update profile.
        /// </summary>
        /// <param name="cmd">
        /// The cmd.
        /// </param>
        public void UpdateProfile(UpdateProfileCommand cmd)
        {
            if (cmd.UpdatedProfile != null)
            {
                var subtype = this.Session.Get<ProfileSubtype>(cmd.UpdatedProfile.SubType);

                var contactType = this.Session.Get<ProfileContactType>(cmd.UpdatedProfile.ContactType);

                var relation = this.Session.Get<ProfileRelation>(cmd.UpdatedProfile.Relation);

                var location = this.Session.Get<Location>(cmd.UpdatedProfile.Location);

                var region = this.Session.Get<Region>(cmd.UpdatedProfile.Region);

                var updatedProfile = this.Session.Get<Profile>(cmd.UpdatedProfile.ID);

                if (updatedProfile != null)
                {
                    updatedProfile.Name = cmd.UpdatedProfile.Name;
                    updatedProfile.CVR = cmd.UpdatedProfile.CVR;
                    updatedProfile.Relation = relation;
                    updatedProfile.Location = location;
                    updatedProfile.ContactType = contactType;
                    updatedProfile.SubType = subtype;
                    updatedProfile.Region = region;
                    updatedProfile.IsRemoteArea = cmd.UpdatedProfile.IsRemoteArea ?? false;
                    updatedProfile.IsEntrepreneur = cmd.UpdatedProfile.IsEntrepreneur ?? false;
                    updatedProfile.Within150km = cmd.UpdatedProfile.Within150Km ?? false;

                    this.Session.Update(updatedProfile);
                    this.Log.CreateLogEntry(
                        updatedProfile.ID,
                        string.Format("Profil '{0}', blev opdateret.", updatedProfile.Name),
                        Enums.LogEntryType.Profile,
                        Enums.LogEntryActionType.Update);
                }
            }
        }

        public void DeleteProfile(DeleteProfileCommand cmd)
        {
            if (cmd != null && cmd.ID.HasValue && cmd.ID.Value != Guid.Empty)
            {
                Profile profile = this.Session.Get<Profile>(cmd.ID.Value);
                if (profile != null)
                {
                    var isReferenced = this.IsProfileReferenced(profile.ID);

                    if (!isReferenced.Item1)
                    {
                        this.Log.CreateLogEntry(
                            profile.ID,
                            string.Format("Profil '{0}', blev slettet.", profile.Name),
                            Enums.LogEntryType.Profile,
                            Enums.LogEntryActionType.Delete);

                        this.Session.Delete(profile);
                    }
                }
            }
        }

        public Tuple<bool, List<Tuple<Guid, LogEntryType>>> IsProfileReferenced(Guid profileID)
        {
            var comments = new List<Tuple<Guid, LogEntryType>>();
            var isReferenced = false;

            
            var p = new List<Guid>() { profileID };

            // Activity.Attendees
            var activitySearch = this.Session.QueryOver<Activity>();
            activitySearch.RootCriteria.CreateAlias("Attendees", "at")
              .Add(Restrictions.In("at.ID", p));
            var activitySearchList = activitySearch.List();
            if (activitySearchList.Any())
            {
                // There exists an activity with this profile as an attendee
                isReferenced = true;
                foreach (var act in activitySearchList)
                {
                    if (act.NA != null)
                    {
                        comments.Add(new Tuple<Guid, LogEntryType>(act.ID, LogEntryType.NA));
                    }
                    else //BDA != null
                    {
                        comments.Add(new Tuple<Guid, LogEntryType>(act.ID, LogEntryType.BDA));
                    }
                }
            }


            // NetworkingActivity.SupportForBusinessAttendance
            var naSearch = this.Session.QueryOver<NetworkingActivity>();
            naSearch.RootCriteria.CreateAlias("SupportForBusinessAttendance", "sfba")
                     .Add(Restrictions.In("sfba.ID", p));
            var naSearchList = naSearch.List();
            if (naSearchList.Any())
            {
                isReferenced = true;
                foreach (var na in naSearchList)
                {
                    var naID = na.ID;
                    var act = this.Session.QueryOver<Activity>().Where(x => x.NA.ID == naID).SingleOrDefault();
                    if(act != null)
                    {
                        comments.Add(new Tuple<Guid, LogEntryType>(act.ID, LogEntryType.NA));
                    }
                }
            }

            // Cluster.Profiles
            var mmSearch = this.Session.QueryOver<Matchmaking>();
            mmSearch.RootCriteria.CreateAlias("Profiles", "pfs")
                     .Add(Restrictions.In("pfs.ID", p));
            var mmSearchList = mmSearch.List();
            if (mmSearchList.Any())
            {
                isReferenced = true;
                foreach (var mm in mmSearchList)
                {
                    comments.Add(new Tuple<Guid, LogEntryType>(mm.ID, LogEntryType.Matchmaking));
                }
            }

            // Cluster.Profiles
            var clusterSearch = this.Session.QueryOver<Cluster>();
            clusterSearch.RootCriteria.CreateAlias("Profiles", "pfs")
                     .Add(Restrictions.In("pfs.ID", p));
            var clusterSearchList = clusterSearch.List();
            if (clusterSearchList.Any())
            {
                isReferenced = true;
                foreach (var cluster in clusterSearchList)
                {
                    comments.Add(new Tuple<Guid, LogEntryType>(cluster.ID, LogEntryType.Cluster));
                }
            }

            // ProjectMeeting.Participants
            var pmSearch = this.Session.QueryOver<ProjectMeeting>();
            pmSearch.RootCriteria.CreateAlias("Participants", "pts")
                     .Add(Restrictions.In("pts.ID", p));
            var pmSearchList = pmSearch.List();
            if (pmSearchList.Any())
            {
                isReferenced = true;
                foreach (var pm in pmSearchList)
                {
                    comments.Add(new Tuple<Guid, LogEntryType>(pm.ID, LogEntryType.ProjectMeeting));
                }
            }
            
            return new Tuple<bool, List<Tuple<Guid, LogEntryType>>>(isReferenced, comments);
        }

        #endregion


    }
}